/*-------------------------------------------------------------------------
 *
 * shmqueue.c
 *	  共享内存链表
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/storage/ipc/shmqueue.c
 *
 * NOTES
 *
 * 管理共享内存中的双向链表的包。唯一棘手的事情是 SHM_QUEUE 通常是一个大
 * 记录中的一个字段。SHMQueueNext 必须返回指向记录本身的指针，而不是指向
 * 记录的 SHMQueue 字段的指针。它需要一个额外的参数，并进行一些额外的
 * 指针运算以便正确执行。
 *
 * NOTE: 这些设置以便它们将来可以转变为宏。
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "storage/shmem.h"


/*
 * ShmemQueueInit -- 使一个新队列的头指向
 *		它自己
 */
void SHMQueueInit(SHM_QUEUE *fc_queue)
{
	Assert(ShmemAddrIsValid(fc_queue));
	fc_queue->prev = fc_queue->next = fc_queue;
}

/*
 * SHMQueueIsDetached -- 如果元素当前不在队列中，则返回true。
 */
bool SHMQueueIsDetached(const SHM_QUEUE *fc_queue)
{
	Assert(ShmemAddrIsValid(fc_queue));
	return (fc_queue->prev == NULL);
}

/*
 * SHMQueueElemInit -- 清除元素的链接
 */
void SHMQueueElemInit(SHM_QUEUE *fc_queue)
{
	Assert(ShmemAddrIsValid(fc_queue));
	fc_queue->prev = fc_queue->next = NULL;
}

/*
 * SHMQueueDelete -- 从队列中移除元素并
 *		关闭链接
 */
void SHMQueueDelete(SHM_QUEUE *fc_queue)
{
	SHM_QUEUE  *fc_nextElem = fc_queue->next;
	SHM_QUEUE  *fc_prevElem = fc_queue->prev;

	Assert(ShmemAddrIsValid(fc_queue));
	Assert(ShmemAddrIsValid(fc_nextElem));
	Assert(ShmemAddrIsValid(fc_prevElem));

	fc_prevElem->next = fc_queue->next;
	fc_nextElem->prev = fc_queue->prev;

	fc_queue->prev = fc_queue->next = NULL;
}

/*
 * SHMQueueInsertBefore -- 在给定的队列元素之前将元素放入队列。
 *		在队列头之前插入元素将元素放置在队列的尾部。
 */
void SHMQueueInsertBefore(SHM_QUEUE *fc_queue, SHM_QUEUE *fc_elem)
{
	SHM_QUEUE  *fc_prevPtr = fc_queue->prev;

	Assert(ShmemAddrIsValid(fc_queue));
	Assert(ShmemAddrIsValid(fc_elem));

	fc_elem->next = fc_prevPtr->next;
	fc_elem->prev = fc_queue->prev;
	fc_queue->prev = fc_elem;
	fc_prevPtr->next = fc_elem;
}

/*
 * SHMQueueInsertAfter -- 在给定的队列元素之后将元素放入队列。
 *		在队列头之后插入元素将元素放置在队列的头部。
 */
void SHMQueueInsertAfter(SHM_QUEUE *fc_queue, SHM_QUEUE *fc_elem)
{
	SHM_QUEUE  *fc_nextPtr = fc_queue->next;

	Assert(ShmemAddrIsValid(fc_queue));
	Assert(ShmemAddrIsValid(fc_elem));

	fc_elem->prev = fc_nextPtr->prev;
	fc_elem->next = fc_queue->next;
	fc_queue->next = fc_elem;
	fc_nextPtr->prev = fc_elem;
}

/*--------------------
 * SHMQueueNext -- 从队列中获取下一个元素
 *
 * 要开始迭代，将队列头作为队列和curElem传递。
 * 如果没有更多元素，则返回NULL。
 *
 * 下一个元素在curElem->next。 如果SHMQueue是
 * 更大结构的一部分，我们希望返回指向
 * 整个结构的指针，而不是指向其SHMQueue字段的指针。
 * 例如，
 * struct {
 *		int				stuff;
 *		SHMQueue		elem;
 * } ELEMType;
 * 当这个元素在队列中时，prevElem->next指向struct.elem。
 * 我们减去linkOffset以获得结构的正确起始地址。
 *
 * 对SHMQueueNext的调用应使用这些参数：
 *	 &(queueHead), &(queueHead), offsetof(ELEMType, elem)
 * 或
 *	 &(queueHead), &(curElem->elem), offsetof(ELEMType, elem)
 *--------------------
 */
Pointer
SHMQueueNext(const SHM_QUEUE *fc_queue, const SHM_QUEUE *fc_curElem, Size fc_linkOffset)
{
	SHM_QUEUE  *fc_elemPtr = fc_curElem->next;

	Assert(ShmemAddrIsValid(fc_curElem));

	if (fc_elemPtr == fc_queue)		/* 返回队列头吗？ */
		return NULL;

	return (Pointer) (((char *) fc_elemPtr) - fc_linkOffset);
}

/*--------------------
 * SHMQueuePrev -- 从队列中获取上一个元素
 *
 * 与SHMQueueNext相同，只是从尾部开始向头部移动。
 * 所有其他评论和用法适用。
 */
Pointer
SHMQueuePrev(const SHM_QUEUE *fc_queue, const SHM_QUEUE *fc_curElem, Size fc_linkOffset)
{
	SHM_QUEUE  *fc_elemPtr = fc_curElem->prev;

	Assert(ShmemAddrIsValid(fc_curElem));

	if (fc_elemPtr == fc_queue)		/* 返回队列头吗？ */
		return NULL;

	return (Pointer) (((char *) fc_elemPtr) - fc_linkOffset);
}

/*
 * SHMQueueEmpty -- 如果队列头是唯一元素则为true，否则为false
 */
bool SHMQueueEmpty(const SHM_QUEUE *fc_queue)
{
	Assert(ShmemAddrIsValid(fc_queue));

	if (fc_queue->prev == fc_queue)
	{
		Assert(fc_queue->next == fc_queue);
		return true;
	}
	return false;
}
